﻿using System;
using System.IO;
using System.Runtime.InteropServices;
using CodeCounsel.SharePoint.RibbonDesigner.Model;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.SharePoint;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.XmlEditor;
using Microsoft.XmlEditor;
using IOleServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider;
using System.Windows;

namespace CodeCounsel.SharePoint.RibbonDesigner
{
    [Guid(GuidList.EditorFactoryString)]
    public class EditorFactory
        : IVsEditorFactory, IDisposable
    {
        ServiceProvider _vsServiceProvider;

        ~EditorFactory()
        {
            Dispose(false);
        }

        public int SetSite(IOleServiceProvider psp)
        {
            _vsServiceProvider = new ServiceProvider(psp);
            return VSConstants.S_OK;
        }

        public int MapLogicalView(
            ref Guid logicalView,
            out string physicalView)
        {
            physicalView = null;
            return (logicalView == VSConstants.LOGVIEWID_Designer ||
                     logicalView == VSConstants.LOGVIEWID_Primary) ?
                 VSConstants.S_OK : 
                 VSConstants.E_NOTIMPL;
        }

        public int CreateEditorInstance(
            uint createFlags, string documentMoniker,
            string physicalView, IVsHierarchy hierarchy,
            uint itemId, IntPtr pExistingDocData,
            out IntPtr ppDocView, out IntPtr ppDocData,
            out string editorCaption, out Guid commandUI,
            out int flags)
        {
            ppDocView = IntPtr.Zero;
            ppDocData = IntPtr.Zero;
            editorCaption = "";
            commandUI = GuidList.EditorFactory;
            flags = 0;
            if ((createFlags & (VSConstants.CEF_OPENFILE | VSConstants.CEF_SILENT)) == 0)
            {
                return VSConstants.E_INVALIDARG;
            }
            XmlModelDocData textBuffer = null;
            if (pExistingDocData != IntPtr.Zero)
            {
                textBuffer = Marshal.GetObjectForIUnknown(pExistingDocData) as XmlModelDocData;
                if (textBuffer == null)
                {
                    return VSConstants.VS_E_INCOMPATIBLEDOCDATA;
                }
            }
            else
            {
                textBuffer = new XmlModelDocData(
                    _vsServiceProvider, documentMoniker);
            }
            XmlEditorService xmlService = _vsServiceProvider.GetService(
                    typeof(XmlEditorService)) as XmlEditorService;
            XmlStore store = xmlService.CreateXmlStore();
            CommandUIModel model = new CommandUIModel(store);
            ISharePointProjectService projectService = (ISharePointProjectService)VSPackage.GetGlobalService(typeof(ISharePointProjectService));
            string path = projectService.SharePointInstallPath;
            path = Path.Combine(path, @"TEMPLATE\GLOBAL\XML\CMDUI.xml");
            try
            {
                model.LoadDefinition(new Uri(path));
                model.AddElementsDefinition(new Uri(documentMoniker, UriKind.RelativeOrAbsolute));
            }
            catch (InvalidRibbonDefinitionException)
            {
                MessageBox.Show("Invalid ribbon");
                return VSConstants.E_FAIL;
            }
            EditorDocView docView = new EditorDocView(model, _vsServiceProvider);
            ppDocView = Marshal.GetIUnknownForObject(docView);
            ppDocData = Marshal.GetIUnknownForObject(textBuffer);
            editorCaption = "[Design]";
            return VSConstants.S_OK;
        }

        public int Close()
        {
            ((IDisposable)this).Dispose();
            return VSConstants.S_OK;
        }

        void IDisposable.Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (_vsServiceProvider != null)
                {
                    _vsServiceProvider.Dispose();
                    _vsServiceProvider = null;
                }
            }
        }
    }
}
